/*
 * MIT License
 *
 * Copyright (c) 2023-present, tangli
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package tony.mybatis.datascope

import java.util.concurrent.Callable
import tony.mybatis.datascope.model.DataScopeValue
import tony.mybatis.datascope.model.ScopedColumn
import tony.mybatis.datascope.model.ScopedTable
import tony.mybatis.datascope.model.SelectionType
import tony.mybatis.datascope.model.current

/**
 * DataScopeContext is
 * @author tangli
 * @date 2025/09/19 08:59
 */
internal class DataScope(
    var userIdColumn: String,
    var orgIdColumn: String,
    val originTables: Set<ScopedTable>,
) : IDataScope {
    private var userIdList: MutableSet<String> = LinkedHashSet()

    override fun <T : Collection<String>> userIdList(userIdList: T): IDataScope =
        apply {
            this.userIdList = userIdList.toMutableSet()
        }

    override fun includeUserId(vararg userId: String): IDataScope =
        apply {
            this.userIdList.addAll(userId)
        }

    override fun <T : Collection<String>> includeUserIdList(userIdList: T): IDataScope =
        apply {
            this.userIdList.addAll(userIdList)
        }

    private var orgIdList: MutableSet<String> = LinkedHashSet()

    override fun <T : Collection<String>> orgIdList(orgIdList: T): IDataScope =
        apply {
            this.orgIdList = orgIdList.toMutableSet()
        }

    override fun includeOrgId(vararg orgId: String): IDataScope =
        apply {
            this.orgIdList.addAll(orgId)
        }

    override fun <T : Collection<String>> includeOrgIdList(orgIdList: T): IDataScope =
        apply {
            this.orgIdList.addAll(orgIdList)
        }

    override fun withOutOrgId(): IDataScope =
        apply {
            this.orgIdList = LinkedHashSet()
        }

    override fun userIdColumn(userIdColumn: String): IDataScope =
        apply {
            this.userIdColumn = userIdColumn
        }

    override fun orgIdColumn(orgIdColumn: String): IDataScope =
        apply {
            this.orgIdColumn = orgIdColumn
        }

    private var selectionType: SelectionType = SelectionType.EXCLUDE
    private var tables: MutableSet<ScopedTable> = LinkedHashSet(originTables)

    override fun <T : Collection<ScopedTable>> excludeTables(tables: T): IDataScope =
        apply {
            this.selectionType = SelectionType.EXCLUDE
            this.tables = LinkedHashSet(originTables.plus(tables))
        }

    override fun <T : Collection<String>> excludeTableNames(tableNames: T): IDataScope =
        apply {
            this.selectionType = SelectionType.EXCLUDE
            this.tables = LinkedHashSet(originTables.plus(tableNames.map { ScopedTable(it) }))
        }

    override fun <T : Collection<ScopedTable>> includeTables(tables: T): IDataScope =
        apply {
            this.selectionType = SelectionType.INCLUDE
            this.tables = tables.toMutableSet()
        }

    override fun <T : Collection<String>> includeTableNames(tableNames: T): IDataScope =
        apply {
            this.selectionType = SelectionType.INCLUDE
            this.tables = tableNames.map { ScopedTable(it) }.toMutableSet()
        }

    private var scopedColumns: Collection<ScopedColumn> = emptyList()

    override fun <T : Collection<ScopedColumn>> scopedColumns(scopedColumns: T): IDataScope =
        apply {
            this.scopedColumns = scopedColumns
        }

    override fun <T> call(func: Callable<T>): T =
        ScopedValue.callWhere<DataScopeValue, T>(
            current,
            dataScopeValue(),
            func
        )

    override fun <T> call(
        dataScopeValue: DataScopeValue,
        func: Callable<T>,
    ): T =
        ScopedValue.callWhere<DataScopeValue, T>(
            current,
            dataScopeValue,
            func
        )

    override fun run(func: Runnable): Unit =
        ScopedValue.runWhere(
            current,
            dataScopeValue(),
            func
        )

    override fun run(
        dataScopeValue: DataScopeValue,
        func: Runnable,
    ): Unit =
        ScopedValue.runWhere(
            current,
            dataScopeValue,
            func
        )

    private fun dataScopeValue(): DataScopeValue =
        DataScopeValue(
            userIdList
                .filter {
                    it.isNotEmpty()
                },
            orgIdList
                .filter {
                    it.isNotEmpty()
                },
            tables,
            userIdColumn,
            orgIdColumn,
            scopedColumns,
            selectionType
        )
}
